/******************************************************************************
*
* This file is part of FreeTrack (http://code.google.com/p/qtfreetrack).
* BY Willy Gardiol (willy@gardiol.org)
*
* FreeTrack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeTrack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
#include "tracktable.h"
#include "layer.h"
#include "layertrack.h"
#include "trackpoint.h"
#include "trackgps.h"
#include "freetrackdialog.h"
#include "layermanager.h"
#include "ftlabel.h"
#include "ftpushbutton.h"

// Say here how many degrees shall map to the screen width at startup
#define INITIAL_WIDTH 0.01

// Zoom steps...
#define SCALE_STEPS 10
#define SCALE_START_STEP 7

extern TrackGps* gps;
TrackTable* trackingTable;

TrackTable::TrackTable(QWidget *parent, LayerManager * l) :
    FreeTrackWidget(parent),
    gps_center(),
    gps_has_fix(false),
    screen_center(),
    screen_scale( 360.0/INITIAL_WIDTH )
{
    trackingTable = this;
    zoom_step = SCALE_START_STEP;
    layers = l;

    // Create zoom-in button
    zoom_in = new FTPushButton(QIcon(":/icons/zoom_in.png"), ICON_SIZE_BIG );
    zoom_in->setBackground( QBrush(QColor(), Qt::NoBrush));
    // Create add waypoint button
    waypoint = new FTPushButton(QIcon(":/icons/waypoint.png"), ICON_SIZE_BIG);
    waypoint->setBackground( QBrush(QColor(), Qt::NoBrush));
    // Create zoom-out button
    zoom_out = new FTPushButton(QIcon(":/icons/zoom_out.png"), ICON_SIZE_BIG);
    zoom_out->setBackground( QBrush(QColor(), Qt::NoBrush));

    // Create follow button
    follow = new FTPushButton( QIcon(":/icons/mirino.png"), ICON_SIZE_BIG );
    // We start in follow mode, always, at startup.
    follow->setCheckable(true);
    follow->setChecked(true);
    follow->setBackground( QBrush(QColor(), Qt::NoBrush));

    // Connect buttons.
    connect( waypoint, SIGNAL(clicked()), this, SLOT(addWaypoint()));
    connect( zoom_in, SIGNAL(clicked()), this, SLOT(zoomIn()));
    connect( zoom_out, SIGNAL(clicked()), this, SLOT(zoomOut()));
    connect( follow, SIGNAL(clicked()), this, SLOT(followMode()));

    // Connect GPS position update to the table
    connect( gps, SIGNAL(gpsUpdate(const TrackPoint&)),
            this, SLOT(gpsUpdated(const TrackPoint&)) );
    connect( gps, SIGNAL(gpsStatusChange(bool)),
            this, SLOT(gpsStatusChanged(bool)) );
}

TrackTable::~TrackTable()
{
}

QLayout* TrackTable::createHorizontal()
{
    QHBoxLayout* hgrid = new QHBoxLayout();
    QVBoxLayout* vgrid = new QVBoxLayout();

    hgrid->addStretch();
    hgrid->addLayout(vgrid);

    vgrid->addWidget( zoom_in );
    vgrid->addStretch();
    vgrid->addWidget( follow );
    vgrid->addWidget( waypoint );
    vgrid->addStretch();
    vgrid->addWidget( zoom_out );
    return hgrid;
}

QLayout* TrackTable::createVertical()
{
    QHBoxLayout* hgrid = new QHBoxLayout();
    QVBoxLayout* vgrid = new QVBoxLayout();

    vgrid->addLayout(hgrid);
    vgrid->addStretch();

    hgrid->addWidget( zoom_in );
    hgrid->addStretch();
    hgrid->addWidget( follow );
    hgrid->addWidget( waypoint );
    hgrid->addStretch();
    hgrid->addWidget( zoom_out );
    return vgrid;
}



void TrackTable::addWaypoint()
{
    FreeTrackWidget* widget = new FreeTrackWidget();
    FreeTrackDialog* dialog = new FreeTrackDialog( widget );

    QVBoxLayout* layout = new QVBoxLayout(widget);
    QLineEdit* name = new QLineEdit();
    QLineEdit* description = new QLineEdit();
    FTLabel* nameL = new FTLabel( tr( "Waypoint name:") );
    FTLabel* descriptionL = new FTLabel( tr( "Description:") );
    FTPushButton* ok = new FTPushButton( tr("Add...") );
    FTPushButton* no = new FTPushButton( tr("Cancel") );
    layout->addStretch();
    layout->addWidget( nameL );
    layout->addWidget( name );
    layout->addWidget( descriptionL );
    layout->addWidget( description );
    layout->addStretch();
    QHBoxLayout* btnG = new QHBoxLayout();
    btnG->addWidget( ok );
    btnG->addWidget( no );
    layout->addLayout( btnG );
    connect( ok, SIGNAL(clicked()),
             widget, SIGNAL(accept()) );
    connect( no, SIGNAL(clicked()),
             widget, SIGNAL(reject()) );

    if ( dialog->exec() == QDialog::Accepted )
    {
         ((LayerTrack*)layers->getLivetrack())->addWaypoint( screen_center, name->text(), description->text() );
         QMessageBox::information( this, tr("Waypoint"), tr("Waypoint added!"));
    }
    delete dialog;
}

void TrackTable::paintEvent ( QPaintEvent * event )
{
    // Create the painter and set paint mode
    QPainter painter( this );
    // The transformation matrix to hold the screen transform
    QTransform matrix;

    // Fix a bug in Symbian (at least for Qt 4.6.3)
    painter.setBackgroundMode ( Qt::OpaqueMode );

    // Since we do a very simply projection we need to compensate for the fact that
    // longitude do shrink towards the poles.
    qreal longitude_correction = (-26.0*screen_center.y()/40.0+111.0)/111.0;

    // Calculate correct scales
    qreal scale_latitude = screen_scale;
    qreal scale_longitude = screen_scale * longitude_correction;

    // this is the ORIGINAL size, in pixels, of the tracking table.
    int screen_width = width();
    int screen_height = height();
    // This is the new scaled size, in lon/lat, this time...
    // (this is how much you can see on the screen)
    QSizeF area_size = QSizeF( screen_width/scale_longitude, screen_height/scale_latitude );

    // NOTE: all the cohordinates are ALWAYS expressed in logical lat/lon. Screen (pixel) cohordinates
    //       are converted multiplying them with the matrix. The "visible area" is the portion of the
    //       lat/lon world which will be visible on the device screen.

    // When "inverting" the vertical ax, we must calculate two different offsets.
    // This is the top/left corner of the visible area rect:
    QPointF area_corner( screen_center.x() - area_size.width()/2.0,
                         screen_center.y() - area_size.height()/2.0 );
    // Instead, the translation offset must be referred to the bottom/left corner of the visible area:
    QPointF translation_offset = QPointF( area_corner.x(),
                                          area_corner.y() + area_size.height() );

    // This is the rectangle "visible" on the screen. Anything outside, is not visible and will be clipped.
    // We calculate this here so that layers can speed up by checking if they are inside the visible area
    // or not...
    QRectF visible_area( area_corner, area_size );

    // Apply the transform to the matrix
    matrix.scale( scale_longitude, -scale_latitude );

    // TODO: scale differently longitude 0 : 111km, 40 : 85km, 90 = 0km; y = -26/40*x + 111;
    // % at 45lat = 73%
    matrix.translate( -translation_offset.x(), -translation_offset.y() );

    // Draw all the layers
    for (int i = layers->countLayers()-1; i >= 0; i--)
        layers->getLayer(i)->paintLayer( painter, matrix, visible_area);
    layers->getLivetrack()->paintLayer( painter, matrix, visible_area);

    // Draw the current GPS point (if any)
    if ( gps_has_fix )
        gps_center.draw( painter, QColor( 0,255,255), matrix );

    int half_screen_width = screen_width / 2;
    int half_screen_height = screen_height / 2;
    int step = qMin( screen_width, screen_height ) / 5;

    // Draw the grid on screen
    painter.setPen( QPen( QBrush( QColor(128,128,128) ), 0) );
    for ( int x = half_screen_width; x > 0; x -= step)
        painter.drawLine( QLine( x, 0, x, screen_height));
    for ( int x = half_screen_width; x < screen_width; x += step)
        painter.drawLine( QLine( x, 0, x, screen_height));
    for ( int y = half_screen_height; y > 0; y -= step)
        painter.drawLine( QLine( 0, y, screen_width, y));
    for ( int y = half_screen_height; y < screen_height; y += step)
        painter.drawLine( QLine( 0, y, screen_width, y));

    // Draw the "cross" in the middle of the screen
    painter.drawPixmap( half_screen_width-24, half_screen_height-24, QPixmap( ":/icons/cross.png" ) );

    // Draw the "zoom" meter
    int zoom_quote = screen_height-step;
    painter.drawPixmap( 2, zoom_quote,
                        QPixmap( ":/icons/zoom_meter.png") );
    painter.drawPixmap( zoom_step*118 / SCALE_STEPS + 4, zoom_quote,
                        QPixmap( ":/icons/zoom_marker.png"));

    qreal lat_num = screen_center.y();
    qreal lon_num = screen_center.x();
    // Draw lat/lon on the display
    painter.drawText( QPoint( 0, screen_height-(step/2)/2),
                      QString("lat:%1%2 lon:%3%4")
                      .arg( fabs(lat_num), 0,'f', 5 ).arg( (lat_num > 0.0) : "N" : "S" )
                      .arg( fabs(lon_num), 0,'f', 5 ).arg( (lon_num > 0.0) : "E" : "W" ) );
}

void TrackTable::zoomIn()
{
    if ( zoom_step < SCALE_STEPS )
    {
        screen_scale *= 2;
        zoom_step++;
        update();
    }
}

void TrackTable::zoomOut()
{
   if ( zoom_step > 0 )
    {
        screen_scale /= 2.0;
        zoom_step--;
        update();
    }
}

void TrackTable::gpsUpdated(const TrackPoint& new_pt)
{   // Store locally the GPS position
    gps_center = new_pt;
    // For what we care, if we get a point we have a fix.
    // NOTE: this, specially at startup, might mean that there is still not a real fix but
    //       this is the last saved position...
    gps_has_fix = true;
    // Update current position, if required
    followMode();
}

void TrackTable::gpsStatusChanged(bool has_fix)
{
    gps_has_fix = has_fix;
}

void TrackTable::followMode()
{   // If we are in "follow" mode, we need to update the screen_center
    if ( follow->isChecked() )
    {   // If we have a GPS update, use it.
        if ( gps_has_fix )
            screen_center = gps_center;
        // Otherwise, use Live Track last point.
        else if ( layers->getLivetrack()->countTrackPoints() > 0 )
            screen_center = layers->getLivetrack()->getLastPoint();
    }
    update();
}

void TrackTable::mousePressEvent ( QMouseEvent * event )
{
    mouse_start = event->posF();
    follow->setChecked(false);
}

void TrackTable::mouseReleaseEvent ( QMouseEvent * event )
{
    QPointF curr_pos = event->posF();
    screen_center += QPointF( (mouse_start.x()-curr_pos.x()) / screen_scale,
                             -(mouse_start.y()-curr_pos.y()) / screen_scale  );
    update();
}

void TrackTable::mouseMoveEvent ( QMouseEvent * event )
{
    mouseReleaseEvent(event);
    mouse_start = event->posF();
}

